using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace GameCore
{
    public class AudioDesc
    {
        public int Id;
        public AudioPlayType type;
        public float volume;
        public AudioSource source;
    }

    public enum AudioPlayType
    {
        Music = 1,
        Sound = 2,
    }

    /// <summary>
    /// 音效管理
    /// </summary>
    public class AudioManager : MonoBehaviour
    {
        public GameObject audioObject;
        private int Id;
        private readonly List<AudioDesc> sources = new();

        private float masterVolume = 1f;  // 主音量（全局控制）
        private float musicVolume = 0.8f; // 音乐独立音量 
        private float soundVolume = 1f;   // 音效独立音量 

        public float MasterVolume
        {
            get => masterVolume;
            set
            {
                if (masterVolume == value)
                    return;
                masterVolume = value;
                UpdateAllVolumes();
            }
        }

        public float MusicVolume
        {
            get => musicVolume;
            set
            {
                if (musicVolume == value)
                    return;
                musicVolume = value;
                UpdateAllVolumes();
            }
        }

        public float SoundVolume
        {
            get => soundVolume;
            set
            {
                if (soundVolume == value)
                    return;
                soundVolume = value;
                UpdateAllVolumes();
            }
        }

        public void LoadVolumeSettings()
        {
            masterVolume = PlayerPrefs.GetFloat("MasterVolume", 1f);
            musicVolume = PlayerPrefs.GetFloat("MusicVolume", 0.8f);
            soundVolume = PlayerPrefs.GetFloat("SoundVolume", 1f);
            UpdateAllVolumes();
        }

        public void SaveVolumeSettings()
        {
            PlayerPrefs.SetFloat("MasterVolume", masterVolume);
            PlayerPrefs.SetFloat("MusicVolume", musicVolume);
            PlayerPrefs.SetFloat("SoundVolume", soundVolume);
            PlayerPrefs.Save();
        }

        private void UpdateAllVolumes()
        {
            foreach (var desc in sources)
                desc.source.volume = desc.volume * (desc.type == AudioPlayType.Music ? musicVolume : soundVolume) * masterVolume;
        }

        public int PlayMusic(string assetPath, float volume = 1f, bool loop = true, bool fadeIn = false, float fadeDuration = 1f, float pitch = 1f)
        {
            var audioClip = Global.Resources.LoadAsset<AudioClip>(assetPath);
            return PlayAudioInternal(audioClip, AudioPlayType.Music, volume * musicVolume * masterVolume, loop, fadeIn, fadeDuration, pitch);
        }

        public int PlayMusic(AudioClip audioClip, float volume = 1f, bool loop = true, bool fadeIn = false, float fadeDuration = 1f, float pitch = 1f)
        {
            return PlayAudioInternal(audioClip, AudioPlayType.Music, volume * musicVolume * masterVolume, loop, fadeIn, fadeDuration, pitch);
        }

        public int PlaySound(string assetPath, float volume = 1f, bool loop = false, bool fadeIn = false, float fadeDuration = 1f, float pitch = 1f)
        {
            var audioClip = Global.Resources.LoadAsset<AudioClip>(assetPath);
            return PlayAudioInternal(audioClip, AudioPlayType.Sound, volume * soundVolume * masterVolume, loop, fadeIn, fadeDuration, pitch);
        }

        public int PlaySound(AudioClip audioClip, float volume = 1f, bool loop = false, bool fadeIn = false, float fadeDuration = 1f, float pitch = 1f)
        {
            return PlayAudioInternal(audioClip, AudioPlayType.Sound, volume * soundVolume * masterVolume, loop, fadeIn, fadeDuration, pitch);
        }

        public int PlayAudio(AudioClip audioClip, AudioPlayType type, float volume = 1f, bool loop = false, bool fadeIn = false, float fadeDuration = 1f, float pitch = 1f, bool oneShot = false)
        {
            return PlayAudioInternal(audioClip, type, volume * (type == AudioPlayType.Music ? musicVolume : soundVolume) * masterVolume, loop, fadeIn, fadeDuration, pitch, oneShot);
        }

        private int PlayAudioInternal(AudioClip audioClip, AudioPlayType type, float volume = 1f, bool loop = false, bool fadeIn = false, float fadeDuration = 1f, float pitch = 1f, bool oneShot = false)
        {
            if (audioClip == null)
                return -1;
            foreach (var desc in sources)
            {
                if (!desc.source.isPlaying) //如果音效剪辑存在 并且 音效没有被播放 则可以执行播放音效
                {
                    desc.type = type;
                    desc.volume = volume;
                    SetAudioDesc(desc, audioClip, volume, loop, fadeIn, fadeDuration, pitch, oneShot);
                    return desc.Id;
                }
            }
            var source = audioObject.AddComponent<AudioSource>();
            var id = Id++;
            var desc1 = new AudioDesc() { Id = id, source = source, type = type, volume = volume };
            sources.Add(desc1);
            SetAudioDesc(desc1, audioClip, volume, loop, fadeIn, fadeDuration, pitch, oneShot);
            return id;
        }

        private void SetAudioDesc(AudioDesc desc, AudioClip audioClip, float volume, bool loop, bool fadeIn = false, float fadeDuration = 1f, float pitch = 1f, bool oneShot = false)
        {
            desc.source.clip = audioClip;
            desc.source.loop = loop;
            desc.source.pitch = pitch;
            if (fadeIn)
            {
                StartCoroutine(FadeAudio(desc.source, fadeDuration, volume));
            }
            else
            {
                desc.source.volume = volume;
                if (oneShot)
                    desc.source.PlayOneShot(audioClip);
                else
                    desc.source.Play();
            }
        }

        public void StopMusic(int Id, bool fadeOut = false, float fadeDuration = 1f)
        {
            StopAudio(Id, AudioPlayType.Music, fadeOut, fadeDuration);
        }

        public void StopSound(int Id, bool fadeOut = false, float fadeDuration = 1f)
        {
            StopAudio(Id, AudioPlayType.Sound, fadeOut, fadeDuration);
        }

        private void StopAudio(int Id, AudioPlayType type, bool fadeOut = false, float fadeDuration = 1f)
        {
            foreach (var desc in sources)
            {
                if (desc.type == type && desc.Id == Id)
                {
                    if (fadeOut)
                        StartCoroutine(FadeOutAudio(desc.source, fadeDuration));
                    else
                        desc.source.Stop();
                }
            }
        }

        public void StopMusicAll(bool fadeOut = false, float fadeDuration = 1f)
        {
            StopAudioAll(AudioPlayType.Music, fadeOut, fadeDuration);
        }

        public void StopSoundAll(bool fadeOut = false, float fadeDuration = 1f)
        {
            StopAudioAll(AudioPlayType.Sound, fadeOut, fadeDuration);
        }

        public void StopAudioAll(bool fadeOut = false, float fadeDuration = 1f)
        {
            StopAudioAll(AudioPlayType.Sound, fadeOut, fadeDuration);
            StopAudioAll(AudioPlayType.Music, fadeOut, fadeDuration);
        }

        private void StopAudioAll(AudioPlayType type, bool fadeOut = false, float fadeDuration = 1f)
        {
            foreach (var desc in sources)
            {
                if (desc.type == type)
                {
                    if (fadeOut)
                        StartCoroutine(FadeOutAudio(desc.source, fadeDuration));
                    else
                        desc.source.Stop();
                }
            }
        }

        private IEnumerator FadeAudio(AudioSource source, float duration, float volume = 1f)
        {
            var startVolume = 0f;
            source.volume = startVolume;
            source.Play();
            while (source.volume < volume)
            {
                source.volume += Time.deltaTime / duration;
                yield return null;
            }
            source.volume = volume;
        }

        private IEnumerator FadeOutAudio(AudioSource source, float duration)
        {
            var startVolume = source.volume;
            while (source.volume > 0)
            {
                source.volume -= Time.deltaTime / duration;
                yield return null;
            }
            source.Stop();
            source.volume = startVolume;
        }
    }
}